#include "Common.h"

initSing(MiscManager);

DLL_EXPORT DBCStore<CharRaceEntry> dbcRaces;
DLL_EXPORT DBCStore<CharClassEntry> dbcClasses;
DLL_EXPORT DBCStore<Skilllineentry> dbcSkills;
DLL_EXPORT DBCStore<SpellEntry> dbcSpells;
DLL_EXPORT DBCStore<AreaEntry> dbcAreaTable;
DLL_EXPORT DBCStore<AreaGroup> dbcAreaGroup;
DLL_EXPORT DBCStore<SpellCastEntry> dbcSpellCasts;
DLL_EXPORT DBCStore<WorldZoneArea> dbcWorldZone;

void MiscManager::LoadPlayers()
{
	QResult* res = CharacterDatabase.SafeQuery("show table status like 'characters'");
	if(!res || !res->NumRows())
		m_playerguids = 0;
	else
	{
		try{
			m_playerguids = res->Fetch()[10];
		}
		catch(...){
			m_playerguids = 0;
		}
		delete res;
	}
	PlayerInfoStorage.Load("playercreateinfo", "uuuuuufffuuuuuuuuuuuuuuu", char_database);
	res = CharacterDatabase.SafeQuery("Select * from characters");
	RecallStorage.Load("recall", "usufff", char_database);
	if(!res || !res->NumRows())
		return;
	do
	{
		PlayerInfo* inf = new PlayerInfo();
		inf->guid = res->Fetch()[0];
		inf->name = new char[strlen(res->Fetch()[2].GetChar()) + 1];
		memcpy(inf->name, res->Fetch()[2].GetChar(), strlen(res->Fetch()[2].GetChar()));
		inf->name[strlen(res->Fetch()[2].GetChar())] = 0;
		Useable::To_Lower(inf->name);
		*inf->name = toupper(*inf->name);
		inf->info = FindPlayerCreateInfo(res->Fetch()[3].GetUI32(), res->Fetch()[4].GetUI32());
		inf->accountid = res->Fetch()[1];
		inf->gender = res->Fetch()[5];
		PlayerInfos[inf->guid] = inf;
	}
	while(res->NextRow());
	delete res;
}

PlayerCreateInfo* MiscManager::FindPlayerCreateInfo(ui8 race, ui8 clas)
{
	PlayerCreateInfoStorage::iterator itr = PlayerInfoStorage.Begin();
	PlayerCreateInfoStorage::iterator end = PlayerInfoStorage.End();

	for( ; itr != PlayerInfoStorage.End(); ++itr)
	{
		if(itr->second->clas == clas && itr->second->race == race)
			return itr->second;
	}

	return NULL;
}

void MiscManager::AddPlayerInfo(PlayerInfo* inf)
{
	PlayerInfos[inf->guid] = inf;
}

Player* MiscManager::CreatePlayer()
{
	ui64 guid = 0;
	m_playerguidlock.Acquire();
	guid = m_playerguids;
	++m_playerguids;
	m_playerguidlock.Release();
	return new Player(guid);
}

void MiscManager::LoadDBC()
{
	dbcRaces.Load("dbc\\ChrRaces.dbc", "uxxxxxxxuxxxxulxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
	dbcClasses.Load("dbc\\ChrClasses.dbc", "uxuxlxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
	dbcSkills.Load("dbc\\SkillLine.dbc", "uuulxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
	dbcSpells.Load("dbc\\Spell.dbc", spellentryFormat);
	dbcAreaTable.Load("dbc\\AreaTable.dbc", "uuuuuxxxuxulxxxxxxxxxxxxxxxxuxxxxxxx");
	dbcAreaGroup.Load("dbc\\AreaGroup.dbc", "uuuuuuuu");
	dbcSpellCasts.Load("dbc\\SpellCastTimes.dbc", "uuxx");
	//dbcWorldZone.Load("dbc\\WorldMapArea.dbc", "uuulffffuu");
	dbcAreaTable.LookupEntry(0);
	sTaxiHandler.LoadDbc();
	printf("\n");
}

PlayerInfo* MiscManager::GetPlayerInfo(ui64 guid)
{
	PlayerInfoStore::iterator itr = PlayerInfos.find((ui32)guid);
	if(itr == PlayerInfos.end())
		return NULL;
	else
		return itr->second;
}

PlayerInfo* MiscManager::GetPlayerInfo(const char* name)
{
	m_playerinflock.Acquire();
	Useable::To_Lower((char*)name);
	PlayerInfoStore::iterator itr = PlayerInfos.begin();
	PlayerInfoStore::iterator end = PlayerInfos.end();

	for( ; itr != PlayerInfos.end(); ++itr)
	{
		if(!stricmp(itr->second->name, name))
		{
			m_playerinflock.Release();
			return itr->second;
		}
	}
	m_playerinflock.Release();
	return NULL;
}

void MiscManager::LoadItems()
{
	QResult* res = CharacterDatabase.SafeQuery("Select MAX(itemguid) from playeritems");
	if(res && res->NumRows())
	{
		m_itemguids = res->Fetch()[0];
		++m_itemguids;
		delete res;
	}
	else
		m_itemguids = 0;
	ItemInfoStorage.Load("items", "uuuussssuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuffuffuuuuuuuuuufuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuusuuuuuuuuuuuuuuuuuuuuuuuuuuuu", char_database);
}

ItemInfo* MiscManager::GetItemInfo(ui32 id)
{
	return ItemInfoStorage.LookupEntry(id);
}

Item* MiscManager::CreateItem(ui32 id, Player* owner)
{
	ItemInfo* inf = GetItemInfo(id);
	if(!inf)
		return 0;
	m_itemguidlock.Acquire();
	Item* itm = new Item(HIGHGUID_TYPE_ITEM, m_itemguids++);
	m_itemguidlock.Release();
	itm->SetOwner(owner);
	itm->SetInfo(inf);
	itm->SaveToDb();
	ItemStorage[itm->GetGUID()] = itm;
	return itm;
}

Item* MiscManager::CreateExistantItem(ui32 lowguid)
{
	Item* itm = new Item(lowguid);
	if(!itm->LoadFromDb())
	{
		delete itm;
		return 0;
	}
	ItemStorage[itm->GetGUID()] = itm;
	return itm;
}

void MiscManager::RemoveItem(ui64 guid)
{
	if(ItemStorage[guid])
		ItemStorage[guid] = 0;
}

void MiscManager::LoadMaps()
{
	WorldMapInfoStorage.Load("worldmap_info", "uuuuuufffusuuuuuuuuu", char_database);
	QResult* res = CharacterDatabase.SafeQuery("Select entry from worldmap_info order by entry asc");
	if(!res || !res->NumRows())
		return;
	do
	{
		ui32 id = res->Fetch()[0];
		WorldMapInfo* i = WorldMapInfoStorage.LookupEntry(id);
		if(!i || i->type)
			continue;
		MapHandler* mhandler = new MapHandler(i, id);
		mhandler->LoadCreatures();
		mhandler->LoadGameObjects();
		ThreadPool.ExecuteTask(mhandler);
		sWorld.m_maphandlers.insert(make_pair(id, mhandler));
		TerrainHandler* th = new TerrainHandler(id);
		th->LoadMapFile();
		mhandler->m_terrainhandler = th;
	}
	while(res->NextRow());
	delete res;
}

void MiscManager::AddPlayer(Player* plr)
{
	m_playerslock.Acquire();
	m_players.insert(plr);
	m_playerslock.Release();
}

Player* MiscManager::GetPlayer(const char* name)
{
	m_playerslock.Acquire();
	set<Player*>::iterator itr = m_players.begin();
	set<Player*>::iterator end = m_players.end();

	for( ; itr != m_players.end(); ++itr)
	{
		if(!stricmp((*itr)->GetName(), name))
		{
			m_playerslock.Release();
			return (*itr);
		}
	}
	m_playerslock.Release();
	return NULL;
}

Player* MiscManager::GetPlayer(ui64 guid)
{
	m_playerslock.Acquire();
	set<Player*>::iterator itr = m_players.begin();
	set<Player*>::iterator end = m_players.end();

	for( ; itr != m_players.end(); ++itr)
	{
		if((*itr)->GetGUID() == guid)
		{
			m_playerslock.Release();
			return (*itr);
		}
	}
	m_playerslock.Release();
	return NULL;
}

void MiscManager::RemovePlayer(const char* name)
{
	m_playerslock.Acquire();
	set<Player*>::iterator itr = m_players.begin();
	set<Player*>::iterator end = m_players.end();

	for( ; itr != m_players.end(); ++itr)
	{
		if(!strcmp((*itr)->GetName(), name))
		{
			m_players.erase(itr);
			m_playerslock.Release();
			if((*itr)->GetMapHandler())
				(*itr)->GetMapHandler()->RemoveObject((*itr));
			return;
		}
	}
	m_playerslock.Release();
}

void MiscManager::RemovePlayer(ui64 guid)
{
	m_playerslock.Acquire();
	set<Player*>::iterator itr = m_players.begin();
	set<Player*>::iterator end = m_players.end();

	for( ; itr != m_players.end(); ++itr)
	{
		if((*itr)->GetGUID() == guid)
		{
			m_players.erase(itr);
			m_playerslock.Release();
			if((*itr)->GetMapHandler())
				(*itr)->GetMapHandler()->RemoveObject((*itr));
			return;
		}
	}
	m_playerslock.Release();
}

void MiscManager::RemovePlayer(Player* plr)
{
	m_playerslock.Acquire();
	m_players.erase(plr);
	m_playerslock.Release();
	if(plr->GetMapHandler())
		plr->GetMapHandler()->RemoveObject(plr);
}

void MiscManager::LoadNpcs()
{
	CreatureBasicStorage.Load("creature_basic", "usssuuuuuuu", char_database);
	CreatureDetailsStorage.Load("creature_details", "uuuuuuufuuu", char_database);
	GossipTextStorage.Load("gossiptexts", "us", char_database);
	NPCTextStorage.Load("text_relation", "uu", char_database);
}

void MiscManager::LoadTrainers()
{
	QResult* res = CharacterDatabase.SafeQuery("Select * from trainerspells");
	if(!res || !res->NumRows())
		return;
	ui32 lastnpc = res->Fetch()[0];
	Trainer* t = new Trainer();
	t->entry = lastnpc;
	do
	{
		QField* rw = res->Fetch();
		ui32 id = rw[0];
		if(id != lastnpc)
		{
			if(t)
				TrainerStorage[lastnpc] = t;
			lastnpc = id;
			t = new Trainer();
			t->entry = lastnpc;
		}
		TrainerSpell* ts = new TrainerSpell;
		ts->id = rw[1];
		ts->level = rw[2];
		ts->price = rw[3];
		ts->required_skill = rw[4];
		ts->required_skill_rank = rw[5];
		ts->required_spell = rw[7];
		t->m_trainerspells.insert(ts);
	}
	while(res->NextRow());
	delete res;
	TrainerStorage[lastnpc] = t;
	Log->Notice("SQLStorage", "Loaded %u trainer(s)", TrainerStorage.size());
}

void MiscManager::LoadWayPoints()
{
	QResult* res = CharacterDatabase.SafeQuery("SELECT * FROM creature_waypoint ORDER BY `npcspawnid` ASC, `waypointid` ASC");
	if(!res || !res->NumRows())
	{
		Log->Notice("SQLStorage", "Loaded 0 waypoints for 0 creatures");
		return;
	}
	ui32 lastnpc = res->Fetch()[0];
	WayPointEntry* w = new WayPointEntry;
	w->entry = lastnpc;
	ui32 count = 0;
	do
	{
		QField* rw = res->Fetch();
		ui32 id = rw[0];
		if(id != lastnpc)
		{
			if(w)
				WayPointStore[lastnpc] = w;
			lastnpc = id;
			w = new WayPointEntry;
			w->entry = lastnpc;
		}
		_WayPointEntry* wpe = new _WayPointEntry;
		wpe->index = rw[1];
		wpe->x = rw[2];
		wpe->y = rw[3];
		wpe->z = rw[4];
		w->waypoints.insert(wpe);
		++count;
	}
	while(res->NextRow());
	delete res;
	WayPointStore[lastnpc] = w;
	Log->Notice("SQLStorage", "Loaded %u waypoints for %u creatures", count, WayPointStore.size());
}

void MiscManager::LoadGuards()
{
	QResult* res = CharacterDatabase.SafeQuery("Select * from guard_pois");
	if(!res || !res->NumRows())
		return;
	ui32 lastnpc = res->Fetch()[0];
	GuardPoi* g = new GuardPoi;
	g->entry = lastnpc;
	do
	{
		QField* rw = res->Fetch();
		ui32 id = rw[0];
		if(id != lastnpc)
		{
			if(g)
				GuardPoiStorage[lastnpc] = g;
			lastnpc = id;
			g = new GuardPoi;
			g->entry = lastnpc;
		}
		GuardPoiEntry* gpe = new GuardPoiEntry;
		gpe->id = rw[1];
		gpe->text = new char[strlen(rw[2].GetChar()) + 1];
		strcpy(gpe->text, rw[2].GetChar());
		gpe->x = rw[3];
		gpe->y = rw[4];
		g->m_pois.insert(make_pair(gpe->id, gpe));
	}
	while(res->NextRow());
	delete res;
	GuardPoiStorage[lastnpc] = g;
	Log->Notice("SQLStorage", "Loaded %u cityguard(s)", GuardPoiStorage.size());
}

void MiscManager::LoadGameObjects()
{
	GameObjectInfoStorage.Load("gameobject_info", "uusuu", char_database);
	GameObjectTeleportStorage.Load("gameobject_teleport", "uuuufff", char_database);
}

void MiscManager::ReloadGameObjectTeleport()
{
	GameObjectTeleportStorage.Clear();	
	GameObjectTeleportStorage.Load("gameobject_teleport", "uuuufff", char_database);
}

void MiscManager::LoadQuests()
{
	QuestInfoStorage.Load("quest", "uusssssssssuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu", char_database);
}

RecallEntry* MiscManager::GetRecallLocation(const char* name)
{
	RecallStorage.AcquireReadLock();
	SQLStorage<RecallEntry>::StorageIterator itr = RecallStorage.Begin();
	SQLStorage<RecallEntry>::StorageIterator end = RecallStorage.End();

	for( ; itr != end; ++itr)
	{
		if(itr->second && itr->second->name && !stricmp(itr->second->name, name))
		{
			RecallStorage.ReleaseReadLock();
			return itr->second;
		}
	}

	RecallStorage.ReleaseReadLock();
	return 0;
}

RecallEntry* MiscManager::GetRecallLocation(ui32 entry)
{
	return RecallStorage.LookupEntry(entry);
}

void MiscManager::AddRecallLocation(RecallEntry* location)
{
	location->id = RecallStorage.GetMaxElement() + 1;
	RecallStorage.AddEntry(location);
}

void RecallEntry::TeleportPlayer(Player* target)
{
	target->TeleportTo(Landmark(x, y, z, mapid));
}

RecallEntry::RecallEntry() : Position(*this)
{
	Position[&RecallEntry::GetPosition];
	Position(&RecallEntry::SetPosition);
}

void RecallEntry::SetPosition(Landmark& npos)
{
	x = npos._x;
	y = npos._y;
	z = npos._z;
	mapid = npos._mapid;
}

Landmark RecallEntry::GetPosition()
{
	return Landmark(x, y, z, mapid);
}